﻿#region License
/*

	Copyright (C) 2008 Oliver Charles, Daniel Hollander

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/
#endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using Moebius.Core;

namespace Moebius.Components
{
    /// <summary>
    /// An <see cref="Entity"/> is some type of object in the <see cref="Components.World"/>
    /// </summary>
    public class Entity
    {
        #region Properties

        readonly List<Type> mRequiredComponents = new List<Type>();
        readonly World mManager;
        private PointF  mPosition;
        private float mScale;
        private float mRotation;
        private DateTime mCreatedAt;

        /// <summary>
        /// Which <see cref="Components.World"/> this <see cref="Entity"/> resides in.
        /// </summary>
        public World World
        {
            get { return mManager; }
        }

        /// <summary>
        /// A list of component types that this entity requires to function.
        /// </summary>
        public ReadOnlyCollection<Type> RequiredComponents
        {
            get { return mRequiredComponents.AsReadOnly(); }
        }

        /// <summary>
        /// The rotation of this <see cref="Entity"/>, in radians
        /// </summary>
        public float Rotation
        {
            get { return mRotation; }
            set { mRotation = value; }
        }

        /// <summary>
        /// The uniform scale of this <see cref="Entity"/> around it's origin.
        /// </summary>
        public float Scale
        {
            get { return mScale; }
            set { mScale = value; }
        }

        /// <summary>
        /// The posisition of the <see cref="Entity"/> in screen space.
        /// </summary>
        public PointF Position
        {
            get { return mPosition; }
            set { mPosition = value; }
        }

        /// <summary>
        /// When the <see cref="Entity"/> was created.
        /// </summary>
        public DateTime CreatedAt
        {
            get { return mCreatedAt; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new Entity in a specific <see cref="World"/>
        /// </summary>
        /// <param name="manager">The <see cref="World"/> to create the <see cref="Entity"/> in.</param>
        public Entity(World manager)
        {
            if(manager == null) throw new NullReferenceException("Cannot create an Entity without a world");

            mManager = manager;
            manager.InjectEntity(this);

            Scale = 1;
        }

        #endregion

        #region Initialize

        /// <summary>
        /// Initialize this <see cref="Entity"/>
        /// </summary>
        /// <remarks>
        /// This is the standard place to initialize all components that make up this <see cref="Entity"/>, by using <see cref="RequestComponent{T}"/>
        /// </remarks>
        /// <param name="resourceFactory">A <see cref="ResourceFactory"/> instance that can be used to load resources</param>
        public virtual void Initialize(ResourceFactory resourceFactory)
        {
            mCreatedAt = DateTime.Now;
        }

        #endregion
        #region Component Management

        /// <summary>
        /// Adds a component to the <see cref="Entity"/>
        /// </summary>
        /// <param name="componentType">The type of component to add to the <see cref="Entity"/></param>
        protected void AddComponent(Type componentType)
        {
            mRequiredComponents.Add(componentType);
        }

        /// <summary>
        /// Request a component from the <see cref="World"/>, that is for this <see cref="Entity"/>
        /// </summary>
        /// <typeparam name="T">The type of component.</typeparam>
        /// <returns>An instance of the component requested, or null</returns>
        protected T RequestComponent<T>() where T : BaseComponent
        {
            return mManager.RequestComponent<T>(this);
        }

        #endregion
    }
}